package Biblioteca;

import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.audio.AudioHeader;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;

/**
 * Clase que implementa una canción de la biblioteca.
 * Para acceder a su metainformación:
 * Título, Artista, Género y Álbum tienen un get y set propio.
 * El resto de la meta-información es accesible mediante getTag(String nombreTag)
 * y setTag(...)
 */
public class Cancion {

    /**
     * Constante para elementos descoconidos. (Género,Artista,Álbum).
     */
    final String cadenaDesconocido = "Desconocido";
    /**
     * Título de la canción.
     */
    private String titulo;
    /**
     * Puntero al objeto género.
     */
    private Genero genero;
    /**
     * Puntero al objeto artista.
     */
    private Artista artista;
    /**
     * Puntero al objeto album.
     */
    private Album album;
    /**
     * Meta-información de la canción.Códec
     */
    private String codec;
    /**
     * Meta-información de la canción.Año
     * @TODO: año String o int?
     */
    private String ano;
    /**
     * Meta-información de la canción.Bitrate
     */
    private String bitrate;
    /**
     * Meta-información de la canción.Canales
     */
    private String canales;
    /**
     * Meta-información de la canción.Pista
     * @TODO: pista String o int?
     */
    private String pista;
    /**
     * Meta-información de la canción.Duracion
     */
    private String duracion;
    /**
     * Contiene el nombre del archivo de audio
     */
    private String ruta;
    /**
     * Contiene el tamaño del archivo de audio en bytes
     */
    private String tamanio;
    /**
     * Contiene la ruta a la caratula
     */
    private String caratula;

    /**
     * Constructora de la clase
     * @param rutaAlArchivo Contiene la ruta al archivo de audio.
     */
    public Cancion(String rutaAlArchivo) {
        this.ruta = rutaAlArchivo;
        this.tamanio = String.valueOf(new File(rutaAlArchivo).length());
    }

    /**
     * Constructora con lo mínimo para que forme parte de la biblioteca.
     * @param rutaAlArchivo Contiene la ruta al archivo de audio.
     * @param titulo String con el título.
     * @param artista Objeto artista de la biblioteca.
     * @param album  Objeto album de la biblioteca.
     * @param genero Objeto genero de la biblioteca.
     */
    public Cancion(String rutaAlArchivo, String titulo, Artista artista, Album album, Genero genero) {
        this.ruta = rutaAlArchivo;
        this.titulo = titulo;
        this.artista = artista;
        this.album = album;
        this.genero = genero;
        this.tamanio = String.valueOf(new File(rutaAlArchivo).length());
        AudioFile f;
        try {
            f = AudioFileIO.read(new File(rutaAlArchivo));
            Tag tag = f.getTag();
            AudioHeader header = f.getAudioHeader();
            duracion = Double.toString(header.getTrackLength());
            bitrate = header.getBitRate();
            codec = header.getFormat();
            canales = header.getChannels();
            ano = tag.getFirst(FieldKey.YEAR);
            pista = tag.getFirst(FieldKey.TRACK);
            caratula = tag.getFirst(FieldKey.COVER_ART.name());
        } catch (Exception ex) {
            Logger.getLogger(Cancion.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Carga la información desde el archivo sin modificar nada de la biblioteca.
     * Construye sus propios objetos álbum, género y artista.
     */
    public void cargarCancionIndependiente() {
        String nombreArtista;
        String nombreAlbum;
        String nombreGenero;
        File archivo;
        archivo = new File(this.ruta);
        try {

            //AudioFile.logger.setLevel(Level.OFF);
            AudioFile f = AudioFileIO.read(archivo);
            Tag tag = f.getTag();
            AudioHeader header = f.getAudioHeader();
            //1.-Leer tags artista, álbum, género.
            //Si alguno no existe (en el archivo), se denomina con cadenaDesconocido.
            nombreArtista = tag.getFirst(FieldKey.ARTIST);
            if (nombreArtista.equals("")) {
                nombreArtista = cadenaDesconocido;
            }
            nombreAlbum = tag.getFirst(FieldKey.ALBUM);
            if (nombreAlbum.equals("")) {
                nombreAlbum = cadenaDesconocido;
            }
            nombreGenero = tag.getFirst(FieldKey.GENRE);
            if (nombreGenero.equals("")) {
                nombreGenero = cadenaDesconocido;
            }
            //2.-Leer título. Si no existe (en el archivo) sustituír con nombre del archivo.
            titulo = tag.getFirst(FieldKey.TITLE);
            if (titulo.equals("")) {
                titulo = archivo.getName();
            }

            //3.-Crear álbum, artista y género para esta canción sin añadirlos a la biblioteca.
            this.album = new Album(nombreAlbum);
            this.artista = new Artista(nombreArtista);
            this.genero = new Genero(nombreGenero);


            //4.-Rellenar su información.

            this.setTag("duracion", Double.toString(header.getTrackLength()));
            this.setTag("bitrate", header.getBitRate());
            this.setTag("codec", header.getFormat());
            this.setTag("canales", header.getChannels());
            this.setTag("año", tag.getFirst(FieldKey.YEAR));
            this.setTag("pista", tag.getFirst(FieldKey.TRACK));
            this.setTag("caratula", tag.getFirst(FieldKey.COVER_ART));


            //5.-Añadir la canción a género,artista,album
            this.album.addCancion(this);
            this.artista.addAlbum(album);
            this.artista.addCancion(this);
            this.genero.addCancion(this);
        } catch (Exception e) {
            System.out.println("Exception en: " + ruta);
            if (e.getMessage() != null) {
                System.out.println(e.getMessage());
            }
            if (e.getLocalizedMessage() != null) {
                System.out.println(e.getLocalizedMessage());
            }
            System.out.println(e.toString());

            //1.-Leer tags artista, álbum, género.
            //Todo desconocido
            nombreArtista = cadenaDesconocido;
            nombreAlbum = cadenaDesconocido;
            nombreGenero = cadenaDesconocido;

            //2.-Leer título. Si no existe (en el archivo) sustituír con nombre del archivo.
            //Se coge directamente el nombre del archivo
            this.titulo = archivo.getName();

            //3.-Crear álbum, artista y género para esta canción sin añadirlos a la biblioteca.
            this.album = new Album(nombreAlbum);
            this.artista = new Artista(nombreArtista);
            this.genero = new Genero(nombreGenero);
            //4.-Construír canción.
            //Rellenar su información.

            this.setTag("duracion", cadenaDesconocido);
            this.setTag("bitrate", cadenaDesconocido);
            this.setTag("codec", cadenaDesconocido);
            this.setTag("canales", cadenaDesconocido);
            this.setTag("año", cadenaDesconocido);
            this.setTag("pista", cadenaDesconocido);
            this.setTag("caratula", cadenaDesconocido);

            //5.-Añadir la canción a género,artista,album
            this.album.addCancion(this);
            this.artista.addAlbum(album);
            this.artista.addCancion(this);
            this.genero.addCancion(this);
        }
    }

    /**
     * modifica el artista
     * @param artista
     */
    public void setArtista(Artista artista) {
        this.artista = artista;
    }

    /**
     * modifica el titulo
     * @param titulo
     */
    public void setTitulo(String titulo) {
        this.titulo = titulo;
    }

    /**
     * modifica el genero
     * @param genero
     */
    public void setGenero(Genero genero) {
        this.genero = genero;
    }

    /**
     * modifica el álbum
     * @param album
     */
    public void setAlbum(Album album) {
        this.album = album;
    }

    /**
     * Asigna el valor especificado en valorTag al tag con el nombre especificado en nombreTag.
     *
     * @param nombreTag
     * @param valorTag
     *
     */
    public void setTag(String nombreTag, String valorTag) {
        if (nombreTag.equals("duracion")) {
            this.duracion = valorTag;
        } else if (nombreTag.equals("bitrate")) {
            this.bitrate = valorTag;
        } else if (nombreTag.equals("codec")) {
            this.codec = valorTag;
        } else if (nombreTag.equals("canales")) {
            this.canales = valorTag;
        } else if (nombreTag.equals("pista")) {
            this.pista = valorTag;
        } else if (nombreTag.equals("año")) {
            this.ano = valorTag;
        } else if (nombreTag.equals("caratula")) {
            this.caratula = valorTag;
        }
    }

    /**
     * @return ruta al archivo de audio.
     */
    public String getRuta() {
        return this.ruta;
    }

    /**
     * @return artista.
     */
    public Artista getArtista() {
        return this.artista;
    }

    /**
     * @return titulo.
     */
    public String getTitulo() {
        return this.titulo;
    }

    /**
     * @return genero.
     */
    public Genero getGenero() {
        return this.genero;
    }

    /**
     * @return album.
     */
    public Album getAlbum() {
        return this.album;
    }

    /**
     * @return
     */
    public String getTamanio() {
        return this.tamanio;
    }

    /**
     * Devuelve el valor de un tag
     * @param nombreTag Nombre del tag del que se quiere su valor
     * @return Valor del tag o null si no existe.
     */
    public String getTag(String nombreTag) {
        if (nombreTag.equals("duracion")) {
            return this.duracion;
        } else if (nombreTag.equals("bitrate")) {
            return this.bitrate;
        } else if (nombreTag.equals("codec")) {
            return this.codec;
        } else if (nombreTag.equals("canales")) {
            return this.canales;
        } else if (nombreTag.equals("pista")) {
            return this.pista;
        } else if (nombreTag.equals("año")) {
            return this.ano;
        } else if (nombreTag.equals("caratula")) {
            return this.caratula;
        } else {
            return null;
        }
    }

    /**
     * Método que devuelve la información de la canción en formato XML para ser escrita en el documento de la biblioteca.
     * Formato: <cancion path="[ruta]"><nombreTag>valorTag</nombreTag> ... </cancion>
     * @param nivelInicial
     * @return String que contiene la información de la canción en XML.
     */
    public String toXML(int nivelInicial) {
        String textoXml;
        String prefijo = "";
        for (int i = 0; i < nivelInicial; i++) {
            prefijo += "\t";
        }
        textoXml = prefijo + "<cancion path=\"" + this.encode(ruta) + "\">\n";
        textoXml += prefijo + "\t<titulo><![CDATA[" + this.titulo + "]]></titulo>\n";
        /*
        textoXml +=  prefijo + "\t<artista><![CDATA[" + this.titulo + "]]></artista>\n";
        textoXml +=  prefijo + "\t<album><![CDATA[" + this.titulo + "]]></album>\n";
        textoXml +=  prefijo + "\t<genero><![CDATA[" + this.titulo + "]]></genero>\n";
         */
        textoXml += prefijo + "\t<codec><![CDATA[" + this.codec + "]]></codec>\n";
        textoXml += prefijo + "\t<canales><![CDATA[" + this.canales + "]]></canales>\n";
        textoXml += prefijo + "\t<bitrate><![CDATA[" + this.bitrate + "]]></bitrate>\n";
        textoXml += prefijo + "\t<duracion><![CDATA[" + this.duracion + "]]></duracion>\n";
        textoXml += prefijo + "\t<pista><![CDATA[" + this.pista + "]]></pista>\n";
        textoXml += prefijo + "\t<anio><![CDATA[" + this.ano + "]]></anio>\n";
        textoXml += prefijo + "\t<genero><![CDATA[" + this.genero.getNombre() + "]]></genero>\n";
        textoXml += prefijo + "</cancion>\n";
        return textoXml;
    }

    @Override
    public String toString() {
//        String out;
//        out="->Canción: "+this.titulo+"\n";
//        out+="\tRuta"+this.ruta+"\n";
//        out+="\tArtista: "+this.artista.getNombre()+"\n";
//        out+="\tAlbum: "+this.album.getNombre()+"\n";
//        out+="\tGenero: "+this.genero.getNombre()+"\n";
//        return out;
        return this.titulo;
    }

    /**
     * Dos canciones son iguales si tienen la misma ruta.
     * @param cancion
     * @return
     */
    public boolean equals(Cancion cancion) {
        return this.getRuta().equals(cancion.getRuta());
    }

    /**
     * Codifica una cadena para evitar los caracteres inválidos en el xml.
     * @param in
     * @return
     */
    public String encode(String in) {
        String out;
        out = in.replace("&", "-8a31-");
        out = out.replace("<", "-8b31-");
        out = out.replace(">", "-8c31-");
        out = out.replace("\"", "-8d31-");
        out = out.replace("'", "-8e31-");
        return out;
    }
}
